#include "Irq.h"
#include "Table.h"
#include "Const.h"
#include "Debug.h"
#include "Pic.h"
#include "Task.h"
#include "Syscall.h"
#include "LibSyscall.h"
#include "utility/Types.h"
#include "utility/Instruction.h"

void DefaultExceptionHandler(ExceptionFrameType *frame)
{
    // Timer interrupt
    if (frame->interruptNumber == 0x20)
    {
        /* code */
        SendPicEOI(frame->interruptNumber);
        TaskTimeTick();
    }
    else
    {
        if (frame->interruptNumber == 13)
        {
            Log("Interruption happened: General Protection Exception");
            if (frame->errorCode & ERR_EXT)
            {
                Log("External to the program interrput");
            }
            else
            {
                Log("Software interrupt");
            }

            if (frame->errorCode & ERR_IDT)
            {
                Log("the index portion of the error code refers to a gate descriptor in the IDT");
            }
            else
            {
                Log("the index refers to a descriptor in the GDT");
            }
            Log("Segment index: %d", frame->errorCode & 0xFFF8);
        }
        else if (frame->interruptNumber == 14)
        {
            Log("Interruption happened: Page Fault");
            if (frame->errorCode & ERR_PAGE_P)
            {
                Log("\t Page-level protection violation: 0x%x", ReadCr2());
            }
            else
            {
                Log("\t Page is not present: 0x%x", ReadCr2());
            }

            if (frame->errorCode & ERR_PAGE_WR) {
                Log("\t The access causing the fault was a read.");
            } else {
                Log("\t The access causing the fault was a write.");
            }
            
            if (frame->errorCode & ERR_PAGE_US) {
                Log("\t A supervisor-mode access caused the fault.");
            } else {
                Log("\t A user-mode access caused the fault.");
            }
        }
        else if (frame->interruptNumber == 0x80)
        {
            //SyscallExceptionHandler(frame);
            return;
        }
        
        

        Log("Exception Info:-------------------------------------");
        uint32_t esp, ss;
        if (frame->cs & 0x7) {
            ss = frame->ss3;
            esp = frame->esp3;
        } else {
            ss = frame->ds;
            esp = frame->esp;
        }
        Log("IRQ: %d, error code: %d.", frame->interruptNumber, frame->errorCode);
        Log("CS: %d\nDS: %d\nES: %d\nSS: %d\nFS:%d\nGS:%d",
            frame->cs, frame->ds, frame->es, ss, frame->fs, frame->gs);
        Log("EAX:0x%x\nEBX:0x%x\nECX:0x%x\nEDX:0x%x\n"
            "EDI:0x%x\nESI:0x%x\nEBP:0x%x\nESP:0x%x\n",
            frame->eax, frame->ebx, frame->ecx, frame->edx,
            frame->edi, frame->esi, frame->ebp, esp);
        Log("EIP:0x%x\nEFLAGS:0x%x\n", frame->eip, frame->eflags);
        Log("Exception Info:-------------------------------------");

        while (1)
        {
            Hlt();
        }
        
    }
}

void SetInterruptionHandler()
{
    // These functions work throungh ASM
    SetIDTGateDesc(IRQ0_DE, GDT_CODE_SELECTOR, (uint32_t)HandleDivideException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ1_DB, GDT_CODE_SELECTOR, (uint32_t)HandleDebugException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ2_NMI, GDT_CODE_SELECTOR, (uint32_t)HandleNMIException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ3_BP, GDT_CODE_SELECTOR, (uint32_t)HandleBreakpointException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ4_OF, GDT_CODE_SELECTOR, (uint32_t)HandleOverflowException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ5_BR, GDT_CODE_SELECTOR, (uint32_t)HandleOutOfBoundException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ6_UD, GDT_CODE_SELECTOR, (uint32_t)HandleInvalidOpcodeException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ7_NM, GDT_CODE_SELECTOR, (uint32_t)HandleDeviceNotAvailableException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ8_DF, GDT_CODE_SELECTOR, (uint32_t)HandleDoubleFaultException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ10_TS, GDT_CODE_SELECTOR, (uint32_t)HandleInvalidTSSException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ11_NP, GDT_CODE_SELECTOR, (uint32_t)HandleSegmentNotPresentException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ12_SS, GDT_CODE_SELECTOR, (uint32_t)HandleStackException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ13_GP, GDT_CODE_SELECTOR, (uint32_t)HandleGeneralProtectionException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ14_PF, GDT_CODE_SELECTOR, (uint32_t)HandlePageFaultException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ16_MF, GDT_CODE_SELECTOR, (uint32_t)HandleFloatingPointException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ17_AC, GDT_CODE_SELECTOR, (uint32_t)HandleAlignmentCheckException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ18_MC, GDT_CODE_SELECTOR, (uint32_t)HandleMachineCheckException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ19_XM, GDT_CODE_SELECTOR, (uint32_t)HandleSIMDException, IDT_GATE_ATTR);
    SetIDTGateDesc(IRQ20_VE, GDT_CODE_SELECTOR, (uint32_t)HandleVirtualizationException, IDT_GATE_ATTR);
    //SetIDTGateDesc(0x80, GDT_CODE_SELECTOR, (uint32_t)HandleSyscallException, GATE_P | GATE_DPL_3 | GATE_TYPE_INTERRUPT | GATE_D_32);
}

uint32_t DisableInterrupts()
{
    uint32_t value = ReadEFLAGS();
    Cli();
    return value;
}

void EnableInterrupts(uint32_t value)
{
    WriteEFLags(value);
}